package com.cmc.cloud.cmclink.doc.service.impl;

import com.cmc.cloud.cmclink.bdt.api.base.dto.req.PortListByCodeReqDTO;
import com.cmc.cloud.cmclink.bdt.api.base.dto.resp.PortListByCodeRespDTO;
import com.cmc.cloud.cmclink.doc.constants.errorconstant.PortDescErrorCodeConstants;
import com.cmc.cloud.cmclink.doc.convert.PortDescConvert;
import com.cmc.cloud.cmclink.doc.entity.PortDescDO;
import com.cmc.cloud.cmclink.doc.mapper.PortDescMapper;
import com.cmc.cloud.cmclink.doc.rpcservice.PortService;
import com.cmc.cloud.cmclink.doc.service.PortDescService;
import com.cmc.cloud.cmclink.doc.vo.portdesc.PortDescCreateReqVO;
import com.cmc.cloud.cmclink.doc.vo.portdesc.PortDescInfoRespVO;
import com.cmc.cloud.cmclink.doc.vo.portdesc.PortDescPageReqVO;
import com.cmc.cloud.cmclink.doc.vo.portdesc.PortDescUpdateReqVO;
import com.cmes.framework.common.exception.util.ServiceExceptionUtil;
import com.cmes.framework.common.pojo.PageResult;
import com.cmes.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.cmes.framework.security.core.util.SecurityFrameworkUtils;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;


/**
 * 港口描述 Service 实现类
 *
 * @author 集运管理员
 */
@Service
@Validated
public class PortDescServiceImpl implements PortDescService {

    private static final String DOC = "DOC";
    private static final String BDT = "BDT";

    @Resource
    private PortDescMapper portDescMapper;

    @Resource
    private PortService portService;

    @Override
    public void createPortDesc(PortDescCreateReqVO createReqVO) {
        this.validatePortAndDescExists(createReqVO.getPortCode(), createReqVO.getPortDesc(), null);
        if (createReqVO.getDefaultFlag() && this.checkPortIsDefault(createReqVO.getPortCode())) {
            throw ServiceExceptionUtil.exception(PortDescErrorCodeConstants.PORT_DEFAULT_EXISTS);
        }
        // 插入
        PortDescDO portDesc = PortDescConvert.INSTANCE.convert(createReqVO);
        portDescMapper.insert(portDesc);
    }

    @Override
    public void updatePortDesc(PortDescUpdateReqVO updateReqVO) {
        // 校验存在id是否存在
        validatePortDescExists(updateReqVO.getId());
        //校验是否违反唯一键
        this.validatePortAndDescExists(updateReqVO.getPortCode(), updateReqVO.getPortDesc(), updateReqVO.getId());
        //校验是否存在默认的港口
        if (updateReqVO.getEnable() && updateReqVO.getDefaultFlag() && this.checkPortIsDefault(updateReqVO.getPortCode(), updateReqVO.getId())) {
            throw ServiceExceptionUtil.exception(PortDescErrorCodeConstants.PORT_DEFAULT_EXISTS);
        }
        // 更新
        PortDescDO updateObj = PortDescConvert.INSTANCE.convert(updateReqVO);
        updateObj.setUpdateTime(LocalDateTime.now());
        updateObj.setUpdater(SecurityFrameworkUtils.getLoginUser().getId() == null ? null : String.valueOf(SecurityFrameworkUtils.getLoginUser().getId()));
        portDescMapper.updateById(updateObj);
    }

    @Override
    public void deletePortDesc(Long id) {
        // 校验存在
        validatePortDescExists(id);
        // 删除
        portDescMapper.deleteById(id);
    }

    private void validatePortAndDescExists(String portCode, String portDesc, Long id) {
        LambdaQueryWrapperX<PortDescDO> lambdaQueryWrapperX = new LambdaQueryWrapperX<>();
        lambdaQueryWrapperX.eqIfPresent(PortDescDO::getPortCode, portCode)
                .eqIfPresent(PortDescDO::getPortDesc, portDesc)
                .neIfPresent(PortDescDO::getId, id);
        if (portDescMapper.selectCount(lambdaQueryWrapperX) > 0) {
            throw ServiceExceptionUtil.exception(PortDescErrorCodeConstants.PORT_DESC_EXISTS);
        }
    }

    private void validatePortDescExists(Long id) {
        if (portDescMapper.selectById(id) == null) {
            throw ServiceExceptionUtil.exception(PortDescErrorCodeConstants.PORT_DESC_NOT_EXISTS);
        }
    }

    @Override
    public PortDescDO getPortDesc(Long id) {
        return portDescMapper.selectById(id);
    }

    @Override
    public List<PortDescDO> getPortDescList(Collection<Long> ids) {
        return portDescMapper.selectBatchIds(ids);
    }

    @Override
    public PageResult<PortDescDO> getPortDescPage(PortDescPageReqVO reqVO) {

        LambdaQueryWrapperX<PortDescDO> lambdaQueryWrapperX = new LambdaQueryWrapperX<>();
        lambdaQueryWrapperX.eqIfPresent(PortDescDO::getPortCode, reqVO.getPortCode())
                .eqIfPresent(PortDescDO::getEnable, reqVO.getEnable())
                .orderByAsc(PortDescDO::getPortCode)
                .orderByAsc(PortDescDO::getSortNo)
                .orderByAsc(PortDescDO::getPortDesc);
        return portDescMapper.selectPage(reqVO, lambdaQueryWrapperX);
    }


    public Boolean checkPortIsDefault(String portCode) {
        LambdaQueryWrapperX<PortDescDO> lambdaQueryWrapperX = new LambdaQueryWrapperX<>();
        lambdaQueryWrapperX.eqIfPresent(PortDescDO::getPortCode, portCode)
                .eq(PortDescDO::getEnable, true)
                .eq(PortDescDO::getDefaultFlag, true);
        return portDescMapper.selectCount(lambdaQueryWrapperX) > 0 ? true : false;
    }

    @Override
    public Boolean checkPortIsDefault(String portCode, Long id) {
        LambdaQueryWrapperX<PortDescDO> lambdaQueryWrapperX = new LambdaQueryWrapperX<>();
        lambdaQueryWrapperX.eqIfPresent(PortDescDO::getPortCode, portCode)
                .eq(PortDescDO::getEnable, true)
                .eq(PortDescDO::getDefaultFlag, true)
                .ne(PortDescDO::getId, id);
        return portDescMapper.selectCount(lambdaQueryWrapperX) > 0 ? true : false;
    }

    @Override
    public List<PortDescInfoRespVO> getPortDescInfo(List<String> port) {
        if (CollectionUtils.isEmpty(port)) {
            return Collections.emptyList();
        }
        List<PortDescInfoRespVO> resultAll = new ArrayList<>(8);
        List<PortDescInfoRespVO> result = new ArrayList<>(8);
        PortListByCodeReqDTO portListByCodeReqDto = new PortListByCodeReqDTO();
        portListByCodeReqDto.setPortCodeList(port);
        List<PortListByCodeRespDTO> portListByCodeRespDtoList = portService.listByCodeList(portListByCodeReqDto);
        LambdaQueryWrapperX<PortDescDO> lambdaQueryWrapperX = new LambdaQueryWrapperX<>();
        lambdaQueryWrapperX.inIfPresent(PortDescDO::getPortCode, port)
                .eqIfPresent(PortDescDO::getEnable, true)
                .orderByAsc(PortDescDO::getPortCode)
                .orderByAsc(PortDescDO::getSortNo)
                .orderByAsc(PortDescDO::getPortDesc);
        List<PortDescDO> portDescDoList = portDescMapper.selectList(lambdaQueryWrapperX);
        if (CollectionUtils.isNotEmpty(portListByCodeRespDtoList)) {
            for (PortListByCodeRespDTO portListByCodeRespDto : portListByCodeRespDtoList) {
                PortDescInfoRespVO portDescInfoRespVO = new PortDescInfoRespVO();
                portDescInfoRespVO.setPortCode(portListByCodeRespDto.getPortCode());
                portDescInfoRespVO.setPortDesc(portListByCodeRespDto.getPortNameEn());
                portDescInfoRespVO.setDefaultFlag(false);
                portDescInfoRespVO.setDataSource(BDT);
                resultAll.add(portDescInfoRespVO);
            }

        }
        if (CollectionUtils.isNotEmpty(portDescDoList)) {
            for (PortDescDO portDescDo : portDescDoList) {
                PortDescInfoRespVO portDescInfoRespVO = new PortDescInfoRespVO();
                portDescInfoRespVO.setPortCode(portDescDo.getPortCode());
                portDescInfoRespVO.setPortDesc(portDescDo.getPortDesc());
                portDescInfoRespVO.setDefaultFlag(portDescDo.getDefaultFlag());
                portDescInfoRespVO.setDataSource(DOC);
                resultAll.add(portDescInfoRespVO);
            }
        }
        if (CollectionUtils.isNotEmpty(resultAll)) {
            result = resultAll.stream().collect(Collectors.toMap(item -> item.getPortCode() + item.getPortDesc(), item -> item, (first, tow) -> first)).values().stream().collect(Collectors.toList());
        }

        return result;
    }
}
